package com.xiaoshuidi.cloud.module.contract.service.contract.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fadada.sdk.base.model.req.UploadTemplateParams;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.fadada.core.FadadaContractServiceApi;
import com.xiaoshuidi.cloud.framework.security.core.LoginUser;
import com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils;
import com.xiaoshuidi.cloud.framework.tenant.core.util.TenantUtils;
import com.xiaoshuidi.cloud.module.contract.controller.admin.contract.vo.contractTemplate.*;
import com.xiaoshuidi.cloud.module.contract.convert.contract.ContractTemplateConvert;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.contract.ContractTemplateDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.contract.ContractTemplateRoomRefDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.contract.TemplateDynamicTablesDO;
import com.xiaoshuidi.cloud.module.contract.dal.dataobject.contract.TemplateKeywardsDO;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.ContractTemplateMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.ContractTemplateRoomMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.TemplateDynamicTablesMapper;
import com.xiaoshuidi.cloud.module.contract.dal.mysql.contract.TemplateKeywardsMapper;
import com.xiaoshuidi.cloud.module.contract.enums.ApiConstants;
import com.xiaoshuidi.cloud.module.contract.enums.contract.ContractTemplateStatusEnum;
import com.xiaoshuidi.cloud.module.contract.enums.contract.ContractTemplateTypeEnum;
import com.xiaoshuidi.cloud.module.contract.service.contract.ContractTemplateService;
import com.xiaoshuidi.cloud.module.contract.util.RedisUtils;
import com.xiaoshuidi.cloud.module.contract.util.date.DateUtils;
import com.xiaoshuidi.cloud.module.contract.util.pdf.PdfUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xiaoshuidi.cloud.module.contract.enums.ApiConstants.*;
import static com.xiaoshuidi.cloud.module.contract.enums.ErrorCodeConstants.*;

/**
 * 合同模板 Service 实现类
 *
 * @author 合房租赁
 */
@RefreshScope
@Service
@Slf4j
public class ContractTemplateServiceImpl implements ContractTemplateService {
    @Value("${fadada.contract_template_prefix}")
    private String CONTRACT_TEMPLATE_PREFIX;

    @Resource
    private ContractTemplateMapper contractTemplateMapper;

    @Resource
    private FadadaContractServiceApi fadadaContractServiceApi;

    @Resource
    private ContractTemplateRoomMapper contractTemplateRoomMapper;

    @Resource
    private TemplateKeywardsMapper templateKeywardsMapper;

    @Resource
    private TemplateDynamicTablesMapper templateDynamicTablesMapper;

    @Override
    public Long createContractTemplate(ContractTemplateCreateReqVO createReqVO) {
        // 插入
        ContractTemplateDO contractTemplate = ContractTemplateConvert.INSTANCE.convert(createReqVO);
        //校验模板名称
        boolean exist = existName(contractTemplate.getTemplateName(), null, contractTemplate.getTemplateType());
        if (exist){
            throw exception(TEMPLATE_NAME_EXIST);
        }

        if (StringUtils.isBlank(contractTemplate.getTemplateType()) || ContractTemplateTypeEnum.RENT_CONTRACT.getCode().equals(contractTemplate.getTemplateType())){
            if (Objects.isNull(contractTemplate.getRoomType())){
                throw exception(RENT_CONTRACT_TEMPLATE_ROOM_TYPE_NOT_NULL);
            }
            // 校验模板必填关键字
            validateTemplateKeywords(createReqVO.getTemplateUrl(), contractTemplate.getId());
            contractTemplate.setTemplateType(ContractTemplateTypeEnum.RENT_CONTRACT.getCode());
        }

        //生成模板编号
        String key = CONTRACT_TEMPLATE_PREFIX + LocalDate.now().format(DateTimeFormatter.ofPattern(DateUtils.FORMAT_NO_DATE));
        String code = RedisUtils.queryMaxCode(CONTRACT_TEMPLATE_REDIS_KEY, key);
        String templateNo = key + code;

        contractTemplate.setTemplateNo(templateNo);
        //合同模板上传法大大
        UploadTemplateParams uploadTemplateParams = new UploadTemplateParams();
        uploadTemplateParams.setTemplateId(contractTemplate.getTemplateNo());
        uploadTemplateParams.setDocUrl(contractTemplate.getTemplateUrl());
        Boolean uploadtemplate = fadadaContractServiceApi.uploadtemplate(uploadTemplateParams);
        if (!uploadtemplate){
            throw exception(TEMPLATE_UPLOAD_PLATFORMS_FAILED);
        }
        contractTemplate.setUploadStatus(ONE);
        contractTemplateMapper.insert(contractTemplate);
        // 返回
        return contractTemplate.getId();
    }

    /**
     * 校验模板必填关键字
     */
    private void validateTemplateKeywords(String templateUrl, Long templateId){
        // 解析
        String[] fieldName = PdfUtils.getFieldName(templateUrl);
        if (null == fieldName || fieldName.length == ZERO){
            throw exception(CONTRACT_TEMPLATE_LACK_REQUIRED_PARAM);
        }
        List<String> templateKeywords = new ArrayList<>(Arrays.asList(fieldName));
        // 必填的合同模板关键字
        LambdaQueryWrapper<TemplateKeywardsDO> keywardsQueryWrapper = new LambdaQueryWrapper<>();
        keywardsQueryWrapper.eq(TemplateKeywardsDO::getRequired, ONE);
        keywardsQueryWrapper.and(qw ->qw.eq(TemplateKeywardsDO::getTemplateId, templateId).or().isNull(TemplateKeywardsDO::getTemplateId));
        List<TemplateKeywardsDO> templateKeywardsList = templateKeywardsMapper.selectList(keywardsQueryWrapper);
        //  合同模板校验关键字埋点数据
        if (CollectionUtils.isNotEmpty(templateKeywardsList)){
            for (TemplateKeywardsDO templateKeywardsDO : templateKeywardsList) {
                if (!templateKeywords.contains(templateKeywardsDO.getKeywords())){
                    log.error("合同模板缺少必填参数：{}", templateKeywardsDO.getKeywords());
                    throw exception(CONTRACT_TEMPLATE_LACK_REQUIRED_PARAM);
                }
            }
        }
    }

    @Override
    public void openOrCloseContractTemplate(ContractTemplateOpenOrCloseReqVO reqVO) {
        // 校验存在
        ContractTemplateDO contractTemplateDO = validateContractTemplateExists(reqVO.getId());

        LambdaUpdateWrapper<ContractTemplateDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ContractTemplateDO::getId, contractTemplateDO.getId());
        if (reqVO.getStatus().intValue() == ContractTemplateStatusEnum.OPEN.getCode().intValue()){
            //开启 合同模板上传法大大
            UploadTemplateParams uploadTemplateParams = new UploadTemplateParams();
            uploadTemplateParams.setTemplateId(contractTemplateDO.getTemplateNo());
            uploadTemplateParams.setDocUrl(contractTemplateDO.getTemplateUrl());
            Boolean uploadtemplate = fadadaContractServiceApi.uploadtemplate(uploadTemplateParams);
            log.info("合同模板上传法大大，请求参数：{}，返回参数：{}", JSONObject.toJSONString(uploadTemplateParams), uploadtemplate);
            if (uploadtemplate){
                updateWrapper.set(ContractTemplateDO::getUploadStatus, ApiConstants.ONE);
            }
        }
        updateWrapper.set(ContractTemplateDO::getStatus, reqVO.getStatus());
        contractTemplateMapper.update(null, updateWrapper);
    }

    @Override
    public void updateContractTemplate(ContractTemplateUpdateReqVO updateReqVO) {
        // 校验存在
        validateContractTemplateExists(updateReqVO.getId());
        // 更新
        ContractTemplateDO updateObj = ContractTemplateConvert.INSTANCE.convert(updateReqVO);
        contractTemplateMapper.updateById(updateObj);
    }

    @Override
    public void deleteContractTemplate(Long id) {
        // 校验存在
        validateContractTemplateExists(id);
        // 校验是否绑定房源
        LambdaQueryWrapper<ContractTemplateRoomRefDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ContractTemplateRoomRefDO::getTemplateId, id);
        List<ContractTemplateRoomRefDO> contractTemplateRoomRefDOS = contractTemplateRoomMapper.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(contractTemplateRoomRefDOS)){
            throw exception(TEMPLATE_ALREADY_BINDING_HOUSE);
        }
        // 删除
        contractTemplateMapper.deleteById(id);
    }

    private ContractTemplateDO validateContractTemplateExists(Long id) {
        ContractTemplateDO contractTemplateDO = contractTemplateMapper.selectById(id);
        if (Objects.isNull(contractTemplateDO)) {
            throw exception(CONTRACT_TEMPLATE_NOT_EXISTS);
        }
        return contractTemplateDO;
    }

    @Override
    public ContractTemplateRespVO getContractTemplate(Long id) {
        ContractTemplateDO contractTemplateDO = contractTemplateMapper.selectById(id);
        ContractTemplateRespVO contractTemplateRespVO = ContractTemplateConvert.INSTANCE.convert(contractTemplateDO);
        // 查询关联门店
        return getTemplateRoomList(contractTemplateRespVO);
    }

    private ContractTemplateRespVO getTemplateRoomList(ContractTemplateRespVO contractTemplateRespVO){
        LambdaQueryWrapper<ContractTemplateRoomRefDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ContractTemplateRoomRefDO::getTemplateId, contractTemplateRespVO.getId());
        List<ContractTemplateRoomRefDO> contractTemplateRoomRefDOS = contractTemplateRoomMapper.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(contractTemplateRoomRefDOS)){
            List<Long> apartmentIds = new ArrayList<>();
            for (ContractTemplateRoomRefDO contractTemplateRoomRefDO : contractTemplateRoomRefDOS) {
                if (Objects.nonNull(contractTemplateRoomRefDO.getApartmentId())){
                    apartmentIds.add(contractTemplateRoomRefDO.getApartmentId());
                }
            }
            contractTemplateRespVO.setApartmentIds(apartmentIds);
        }
        return contractTemplateRespVO;
    }

    @Override
    public PageResult<ContractTemplateRespVO> getContractTemplatePage(ContractTemplatePageReqVO pageReqVO) {
        Page<ContractTemplateDO> page = new Page<>(pageReqVO.getPageNo(),pageReqVO.getPageSize());
        LambdaQueryWrapper<ContractTemplateDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Objects.nonNull(pageReqVO.getStatus()), ContractTemplateDO::getStatus, pageReqVO.getStatus());
        queryWrapper.like(StringUtils.isNotBlank(pageReqVO.getTemplateName()), ContractTemplateDO::getTemplateName, pageReqVO.getTemplateName());
        queryWrapper.like(StringUtils.isNotBlank(pageReqVO.getTemplateNo()), ContractTemplateDO::getTemplateNo, pageReqVO.getTemplateNo());
        queryWrapper.like(StringUtils.isNotBlank(pageReqVO.getTemplateType()), ContractTemplateDO::getTemplateType, pageReqVO.getTemplateType());
        queryWrapper.orderByDesc(ContractTemplateDO::getCreateTime);
        page = contractTemplateMapper.selectPage(page, queryWrapper);
        List<ContractTemplateRespVO> contractTemplateRespVOS = ContractTemplateConvert.INSTANCE.convertList(page.getRecords());
        for (ContractTemplateRespVO contractTemplateRespVO : contractTemplateRespVOS) {
            this.getTemplateRoomList(contractTemplateRespVO);
        }
        return new PageResult<>(contractTemplateRespVOS,  page.getTotal());
    }

    /**
     * 校验是否存在已绑定合同模板的门店
     */
    void validateContractTemplateRefRoom(ContractTemplateRelHouseReqVO reqVO, Integer roomType, String templateType){
        if (CollectionUtils.isNotEmpty(reqVO.getApartmentIds())){
            LambdaQueryWrapper<ContractTemplateRoomRefDO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(ContractTemplateRoomRefDO::getApartmentId, reqVO.getApartmentIds());
            queryWrapper.ne(ContractTemplateRoomRefDO::getTemplateId, reqVO.getTemplateId());
            queryWrapper.eq(ContractTemplateRoomRefDO::getRoomType, roomType);
            queryWrapper.eq(ContractTemplateRoomRefDO::getTemplateType, templateType);
            List<ContractTemplateRoomRefDO> contractTemplateRoomRefDOList = contractTemplateRoomMapper.selectList(queryWrapper);
            if (CollectionUtils.isNotEmpty(contractTemplateRoomRefDOList)){
                throw exception(ROOM_ALREADY_REF_OTHER_TEMPLATE);
            }
        }
    }

    @Override
    public void relationHouse(ContractTemplateRelHouseReqVO reqVO) {
        // 校验存在
        ContractTemplateDO contractTemplate = validateContractTemplateExists(reqVO.getTemplateId());
        if (ContractTemplateTypeEnum.RENT_CONTRACT.getCode().equals(contractTemplate.getTemplateType())){
            // 校验门店是否关联其他合同模板（一个门店可关联两种类型的模板，且一种只有一份）
            validateContractTemplateRefRoom(reqVO,contractTemplate.getRoomType(),contractTemplate.getTemplateType());
        }
        //删除原关联
        LambdaQueryWrapper<ContractTemplateRoomRefDO> deleteWrapper = new LambdaQueryWrapper<>();
        deleteWrapper.eq(ContractTemplateRoomRefDO::getTemplateId, reqVO.getTemplateId());
        contractTemplateRoomMapper.delete(deleteWrapper);

        //保存新关联
        List<ContractTemplateRoomRefDO> contractTemplateRoomRefList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(reqVO.getApartmentIds())){
            for (Long apartmentId : reqVO.getApartmentIds()) {
                ContractTemplateRoomRefDO contractTemplateRoomRef = new ContractTemplateRoomRefDO();
                contractTemplateRoomRef.setTemplateId(reqVO.getTemplateId());
                contractTemplateRoomRef.setApartmentId(apartmentId);
                contractTemplateRoomRef.setRoomType(contractTemplate.getRoomType());
                contractTemplateRoomRef.setTemplateType(contractTemplate.getTemplateType());
                contractTemplateRoomRefList.add(contractTemplateRoomRef);
            }
        }
        if (CollectionUtils.isNotEmpty(contractTemplateRoomRefList)){
            contractTemplateRoomMapper.insertBatch(contractTemplateRoomRefList);
        }
    }

    @Override
    public boolean existName(String templateName, Long id, String templateType){
        if (StringUtils.isBlank(templateType)){
            templateType = ContractTemplateTypeEnum.RENT_CONTRACT.getCode();
        }
        LambdaQueryWrapper<ContractTemplateDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ContractTemplateDO::getTemplateName, templateName);
        queryWrapper.eq(ContractTemplateDO::getTemplateType, templateType);
        if (Objects.nonNull(id)){
            queryWrapper.ne(ContractTemplateDO::getId, id);
        }
        return CollectionUtils.isNotEmpty(contractTemplateMapper.selectList(queryWrapper));
    }



    @Override
    public ContractTemplateDO getTemplateByRoomId(Long apartmentId, Integer roomType, String templateType) {
        if (Objects.isNull(apartmentId) || Objects.isNull(templateType)){
            return null;
        }
        //查询房源绑定的合同模板
        LambdaQueryWrapper<ContractTemplateRoomRefDO> houseTemplateRefQueryWrapper = new LambdaQueryWrapper<>();
        houseTemplateRefQueryWrapper.eq(ContractTemplateRoomRefDO::getApartmentId, apartmentId);
        houseTemplateRefQueryWrapper.eq(Objects.nonNull(roomType),ContractTemplateRoomRefDO::getRoomType, roomType);
        houseTemplateRefQueryWrapper.eq(ContractTemplateRoomRefDO::getTemplateType, templateType);
        List<ContractTemplateRoomRefDO> contractTemplateRoomRefDOS = contractTemplateRoomMapper.selectList(houseTemplateRefQueryWrapper);
        if (CollectionUtils.isEmpty(contractTemplateRoomRefDOS)) {
            //房源未绑定合同模板
            throw exception(APARTMENT_NOT_REF_CONTRACT_TEMPLATE);
        }
        ContractTemplateRoomRefDO currentTempRef = contractTemplateRoomRefDOS.get(ZERO);
        return contractTemplateMapper.selectById(currentTempRef.getTemplateId());
    }

    @Override
    public List<TemplateKeywardsDO> getTemplateKeyWordsByTemplateId(Long templateId,String templateType) {
        LambdaQueryWrapper<TemplateKeywardsDO> keywardsQueryWrapper = new LambdaQueryWrapper<>();
        keywardsQueryWrapper.eq(StringUtils.isNotBlank(templateType), TemplateKeywardsDO::getTemplateType, templateType);
        keywardsQueryWrapper.and(qr -> qr.eq(Objects.nonNull(templateId), TemplateKeywardsDO::getTemplateId, templateId)
                        .or().isNull(TemplateKeywardsDO::getTemplateId));
        return templateKeywardsMapper.selectList(keywardsQueryWrapper);
    }

    @Override
    public List<TemplateDynamicTablesDO> getTemplateDynamicTablesByTemplateId(Long templateId, String templateType) {
        LambdaQueryWrapper<TemplateDynamicTablesDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TemplateDynamicTablesDO::getTemplateType, templateType);
        queryWrapper.and(qr -> qr.eq(TemplateDynamicTablesDO::getTemplateId, templateId)
                .or().isNull(TemplateDynamicTablesDO::getTemplateId));
        return templateDynamicTablesMapper.selectList(queryWrapper);
    }

    @Override
    public List<Long> getSelectedApartments(Long templateId, String templateType) {
        ContractTemplateDO contractTemplate = validateContractTemplateExists(templateId);
        LambdaQueryWrapper<ContractTemplateRoomRefDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ContractTemplateRoomRefDO::getTemplateType, templateType);
        if (ContractTemplateTypeEnum.RENT_CONTRACT.getCode().equals(templateType)){
            queryWrapper.eq(ContractTemplateRoomRefDO::getRoomType, contractTemplate.getRoomType());
        }
        List<ContractTemplateRoomRefDO> contractTemplateRoomRefList = contractTemplateRoomMapper.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(contractTemplateRoomRefList)){
            return contractTemplateRoomRefList.stream().map(ContractTemplateRoomRefDO::getApartmentId).collect(Collectors.toList());
        }
        return null;
    }

    @Override
    public PageResult<TemplateKeywardsDO> getContractTemplateKeywordsPage(ContractTemplateKeywordsPageReqVO pageReqVO) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        Long tenantId = null;
        if (Objects.nonNull(loginUser)){
            tenantId = loginUser.getTenantId();
        }
        Long finalTenantId = tenantId;
        return TenantUtils.executeIgnoreAndReturn(()->{
            Page<TemplateKeywardsDO> page = new Page<>(pageReqVO.getPageNo(),pageReqVO.getPageSize());
            LambdaQueryWrapper<TemplateKeywardsDO> keywardsQueryWrapper = new LambdaQueryWrapper<>();
            keywardsQueryWrapper.eq(StringUtils.isNotBlank(pageReqVO.getTemplateType()), TemplateKeywardsDO::getTemplateType, pageReqVO.getTemplateType());
            if (Objects.nonNull(finalTenantId)){
                keywardsQueryWrapper.and(qr -> qr.eq(TemplateKeywardsDO::getTenantId, finalTenantId)
                        .or().isNull(TemplateKeywardsDO::getTenantId));
            }else {
                keywardsQueryWrapper.isNull(TemplateKeywardsDO::getTenantId);
            }
            keywardsQueryWrapper.eq(Objects.nonNull(pageReqVO.getType()), TemplateKeywardsDO::getType, pageReqVO.getType());
            keywardsQueryWrapper.like(StringUtils.isNotBlank(pageReqVO.getName()), TemplateKeywardsDO::getName, pageReqVO.getName());
            keywardsQueryWrapper.like(StringUtils.isNotBlank(pageReqVO.getKeywords()), TemplateKeywardsDO::getKeywords, pageReqVO.getKeywords());
            keywardsQueryWrapper.like(StringUtils.isNotBlank(pageReqVO.getRemark()), TemplateKeywardsDO::getRemark, pageReqVO.getRemark());
            keywardsQueryWrapper.orderByDesc(TemplateKeywardsDO::getCreateTime);
            page = templateKeywardsMapper.selectPage(page, keywardsQueryWrapper);
            return new PageResult<>(page.getRecords(),  page.getTotal());
        });
    }
}
